// ISO C++ libs
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <string>

// Std libs 
#include <pthread.h>
#include <unistd.h>

// libs specific for QNX
#include <sys/neutrino.h>
#include <sys/neutrino.h>
#include <sys/dispatch.h>

// headers specific for projects 
#include "Airport.h"
#include "Aircraft.h"
#include "Runway.h"
#include "MsgStruct.h"

#define DATABASE_NAME "database"


// semaphore for mutual exclusion, to implement critical section
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

name_attach_t *serverAttachName;
int whichClient;
aircraftList al;
packet_t reply;
packet_t messag;
Airport *a;

char fileAirportName[50];
int fileX,fileY;
int fileCapacity;
int fileRange;
int fileNumOfRunways;
int fileState;
	

//initialArg_struct GUIinit;
	

void *AACScommunication(void *arg);

void *interModulesCommunication(void *arg);

void *sendInitializationMessagetoGUI2(void *arg);

int sendInitializationMessagetoGUI(char aName[50],int X,int Y,int r,int c,int run,int st);

void sendInitializationMessagetoDB();

void prepareAirportData(packet_t &packet);

void prepareRunwaysData(packet_t &packet);

void packingInfoToGUI(aircraftList &aList, int i);

void transformIntoGUICoordinates(int X,int Y,int &x,int &y,int R,int r,int Xpos,int Ypos);


void updateInfoAboutAircraft(packet_t &packet);
void deleteInfoAboutAircraft(packet_t &packet);

int main(int argc, char *argv[])
{
    pthread_t AACScommThread;
    pthread_t interModcommThread;
    Runway *r;
    Aircraft *plane;
    FILE *plik;
    int counter;
	
    // server starts here...
	
	
    if(argc<2)
    {
	std::cout << "There is a problem with opening config file!" << std::endl;
	std::cout << "You should use: airport name_of_configfile" << std::endl;
	exit(1);
    }
	
	// hello, I'm alive - message to the world :)
    system("clear");
    std::cout << "Welcome in AACS" << std::endl;
    std::cout << "Airport & Aircraft Control System" << std::endl << std::endl;

    
    // get airport's parameters from config file
    plik = fopen(argv[1],"r");
    fscanf(plik, "%s\n", fileAirportName);
    fscanf(plik, "%d\n", &fileX);
    fscanf(plik, "%d\n", &fileY);
    fscanf(plik, "%d\n", &fileRange);
    fscanf(plik, "%d\n", &fileCapacity);
    fscanf(plik, "%d\n", &fileNumOfRunways);
    fscanf(plik, "%d\n", &fileState);
    
	
    //register new airport
    a = new Airport(fileAirportName,
		    fileX,
		    fileY,
		    fileRange,
		    fileCapacity,
		    fileNumOfRunways,
		    fileState);
		    
    std::cout << "::AIRPORT::" << std::endl;
    a->printInfo();
    std::cout << "Has been registered" << std::endl << std::endl;
        
    
    
    // initializint runways on airport
    for(counter=1;counter<=fileNumOfRunways;counter++)
    {
	r = new Runway(counter,false);
	a->addRunway(*r);
    }
    
    // show runways' status
    std::cout << "Runways status:" << std::endl;
    a->showRunwaysStatus();
    std::cout << std::endl;

    // add 6 airplanes
    plane = new Aircraft(1,"BNG122",120,40,666,1);
    a->addAircraft(*plane);
    plane->getInfo();
    std::cout << "Has been created" << std::endl;
    std::cout << "List of airplanes:" << std::endl;
    //a->showAircraftsList();
    std::cout << std::endl; 
	
    plane = new Aircraft(2,"TFD349",140,160,100,1);
    a->addAircraft(*plane);
    plane->getInfo();
    std::cout << "Has been created" << std::endl;
    std::cout << "List of airplanes:" << std::endl;
    //a->showAircraftsList();
    std::cout << std::endl;
	
    plane = new Aircraft(3,"DAB891",80,130,320,1);
    a->addAircraft(*plane);
    plane->getInfo();
    std::cout << "Has been created" << std::endl;
    std::cout << "List of airplanes:" << std::endl;
    //a->showAircraftsList();
    std::cout << std::endl;
    
    plane = new Aircraft(4,"AST343",40,40,444,1);
    a->addAircraft(*plane);
    plane->getInfo();
    std::cout << "Has been created" << std::endl;
    std::cout << "List of airplanes:" << std::endl;
    //a->showAircraftsList();
    std::cout << std::endl;
    
    plane = new Aircraft(5,"AAA333",101,101,453,1);
    a->addAircraft(*plane);
    plane->getInfo();
    std::cout << "Has been created" << std::endl;
    std::cout << "List of airplanes" << std::endl; 
    //a->showAircraftsList();
    std::cout << std::endl;
    
    plane = new Aircraft(6,"sdsd3",199,101,333,1);
    a->addAircraft(*plane);
    plane->getInfo();
    std::cout << "Has been created" << std::endl;
    std::cout << "List of airplanes" << std::endl;
    a->showAircraftsList();
    std::cout << std::endl;
	
    delete plane;
    	
    
    
    if ((serverAttachName = name_attach(NULL,fileAirportName,NAME_FLAG_ATTACH_GLOBAL))==NULL)
    {
	std::cout << "I can't create channel" << std::endl;
	return 1;
    }
    else
    {
	std::cout << "Channel has been created" << std::endl;
    }    
    
    
    // now we're sending initialization message to DataBase module
    //sendInitializationMessagetoDB();

    std::cout << "Main thread" << std::endl;
    
    
    
    std::cout << a->getAircraftsNumber() << std::endl;
    //strcpy(messag.samolot.aircraftName,"AST3443");
    //updateInfoAboutAircraft(messag);
    
    //strcpy(messag.samolot.aircraftName,"TFD349");
    //deleteInfoAboutAircraft(messag);
    strcpy(messag.samolot.aircraftName,"sdsd3");
    deleteInfoAboutAircraft(messag);
    //strcpy(messag.samolot.aircraftName,"DAB891");
    //deleteInfoAboutAircraft(messag);
    strcpy(messag.samolot.aircraftName,"AST343");
    deleteInfoAboutAircraft(messag);
    strcpy(messag.samolot.aircraftName,"AAA333");
    deleteInfoAboutAircraft(messag);
    
    
    std::cout << "after " <<std::endl;
    a->showAircraftsList();
    std::cout << a->getAircraftsNumber() << std::endl;
    
    

    //pthread_mutex_init(&mutex,NULL);
    
    int rcvid;
    int i=0;
    Aircraft dummyAircraft;

    while(1)
    {
	rcvid = MsgReceive(serverAttachName->chid, &messag, sizeof(messag), NULL);
	if(rcvid == -1)
	{
	    break; // fck'n hell
	}
	else
	{
	    std::cout << "I've received: " << messag.who << " " << messag.type << std::endl;
	    switch(messag.who)
	    {
		case GUI:
		    switch(messag.type)
		    {
			case GUI_WANT_AIRPORT_DATA:
			{
				std::cout << "GUI wants data about airport" << std::endl;
				prepareAirportData(reply);
				MsgReply(rcvid,NULL,&reply,sizeof(reply));
				std::cout << "Data about " << fileAirportName << " has been send to GUI" << std::endl;
				break;
			}
			case GUI_WANT_AIRCRAFT_DATA:
			{
				std::cout << "Gui wants data about aircrafts" << std::endl;
				packingInfoToGUI(al,i);
				i++;	
				reply.who=AIRPORT;
				reply.type=COMMAND_OK;
				reply.listaSamolotow = al;
				MsgReply(rcvid,NULL,&reply,sizeof(reply));
				std::cout << "Data about aircrafts has been send to GUI" << std::endl;
				break;
			}
			case GUI_WANT_RUNWAY_DATA:
			{
				break;
			}
			case GUI_SEND_CONTROL_DATA:
			{
				break;
			}
			default:
			{
				reply.who=-1;
				reply.type=-1;
				MsgReply(rcvid,NULL,&reply,sizeof(reply));
				break;
			}
		    }
		    break;
		case AI:
		    switch(messag.type)
		    {
			case AI_WANT_AIRPORT_DATA:
			{
				std::cout << "AI wants data about airport" << std::endl;
				prepareAirportData(reply);
				MsgReply(rcvid,NULL,&reply,sizeof(reply));
				std::cout << "Data about" << fileAirportName << " has been send to AI module" << std::endl; 
				break;
			}
			case AI_WANT_AIRCRAFT_DATA:
			{
				std::cout << "AI wants data about aircrafts" << std::endl;
				packingInfoToGUI(al,0);
				reply.who=AIRPORT;
				reply.type=COMMAND_OK;
				reply.listaSamolotow = al;
				MsgReply(rcvid,NULL,&reply,sizeof(reply));
				std::cout << "Data about aircrafts has been send to AI module" << std::endl;
				break;
			}
			case AI_SEND_AIRCRAFT_DATA:
			{
				std::cout << "AI sends aircraft data" << std::endl;
				reply.who=AIRPORT;
				reply.type=COMMAND_OK;
				MsgReply(rcvid,NULL,&reply,sizeof(reply));
				std::cout << "I've received sorted list of aircrafts from AI" << std::endl; 			
				break;
			}
			default:
			{
				reply.who=-1;
				reply.type=-1;
				MsgReply(rcvid,NULL,&reply,sizeof(reply));
				break;
			}
		    }
		    break;
		case AIRPLANE:
		    switch(messag.type)
		    {
			case 1: // aircraft sending data about itself
			{
			    std::cout << "SASS" << std::endl;
			    updateInfoAboutAircraft(messag);
			    reply.who=AIRPORT;
			    reply.type=COMMAND_OK;
			    MsgReply(rcvid,NULL,&reply,sizeof(reply));
			    break;
			}
			case 2: // aircraft sending info that it goes outside radar range
			{
			    std::cout << "SASASDSDS" << std::endl; 
			    deleteInfoAboutAircraft(messag);
			    reply.who=AIRPORT;
			    reply.who=COMMAND_OK;
			    MsgReply(rcvid,NULL,&reply,sizeof(reply));
			    break;
			}
			default:
			    break;
		    }
		break;
		
		default:
		    reply.who=-1;
		    reply.type=-1;
		    MsgReply(rcvid,NULL,&reply,sizeof(reply));
		    break;    
		    
	    }
	}
    }   
    
	
    //pthread_create(&AACScommThread,NULL,&AACScommunication,NULL);
    //pthread_create(&interModcommThread,NULL,&interModulesCommunication,NULL);
    //pthread_join(AACScommThread,NULL);
    //pthread_join(interModcommThread,NULL);
				   				
    delete a;
	
    name_detach(serverAttachName,0);
	
    return EXIT_SUCCESS;	
	
}


/**
* This thread connects 
*/

/*
void *AACScommunication(void *arg)
{
    while(1)
    {
	pthread_mutex_lock(&mutex);
	std::cout << "Poczatek komunikacji AACS" << std::endl;
				
	whichClient = MsgReceive(serverAttachName->chid,&msg,sizeof(msg),NULL);	
	if(whichClient>0)
	{
	    std::cout << "Odebralem z clienta message'a" << std::endl;
	    std::cout << msg.type << std::endl;
	}
	else
	{
	    std::cout << "message nie dostarczony" << std::endl;
	}
		
	    msg.type=1;		
	    MsgReply(whichClient, NULL, &msg, sizeof(msg));
	
	    std::cout << "Koniec komunikacji AACS" << std::endl; 		
	    pthread_mutex_unlock(&mutex);
	    sleep(1);
	}
}
*/


void *interModulesCommunication(void *arg)
{
    while(1)
    {
	pthread_mutex_lock(&mutex);
	std::cout << "Poczatek komunikacji interModules" << std::endl;
	// do sth...
	std::cout << "Koniec komunikacji interModules" << std::endl;
	pthread_mutex_unlock(&mutex);
	sleep(1);
    }
}



/**
* Function sends some initialization data to DataBase. It should be
* the first move for airport, because information about airport
* should be send as fast as it's possible - all other modules may need
* infos about it.
*/
void sendInitializationMessagetoDB()
{
    int fd;
    
    if((fd = name_open(DATABASE_NAME , NAME_FLAG_ATTACH_GLOBAL)) == -1)
    {
	printf("Ale hujnia ta db\n");
	exit(1);
    }
    printf("JEST DB !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
    messag.who = AIRPORT;
    messag.type = AIRPORT_WANT_PUT_ITS_DATA;
    MsgSend(fd, &messag, sizeof(messag), &reply, sizeof(reply));
    
    if(reply.who == DATABASE && reply.type == COMMAND_OK)
    {
	std::cout << "Data about airport has been add into database" << std::endl;
    }
    else
    {
	std::cout << "Data about airport has NOT been add into database" << std::endl;
    }
}



/**
* Function packs info about all aircrafts within in range of airport
* into structure. This structure after all is ready to be send to GUI
* @param &aList Structure of aircrafts' list which will be send.
*/
void packingInfoToGUI(aircraftList &aList,int i) // it shouldn't be i here - only for testing
{

    int counter; // counter counts :)
    int number =a->getAircraftsNumber(); // pack number of aircrafts
    Aircraft dummyAircraft; // dummyAircraft
    int dummyX, dummyY;
    int resX=0,resY=0;
    
    std::cout << "I'm sending " << number << " aircraft(s)" << std::endl;
    for(counter=0 ; counter < number ; counter++)
    {
	dummyAircraft = a->getAircraftStructure(counter); // take a structure of aircraft no. 'counter'
	aList.noOfAircrafts=number;
	strcpy(aList.a[counter].aircraftName,dummyAircraft.getAircraftName());
	aList.a[counter].aircraftID = dummyAircraft.getUniqueID();
	dummyX = dummyAircraft.getX();
	dummyY = dummyAircraft.getY();
	transformIntoGUICoordinates(dummyX,dummyY,resX,resY,fileRange,340,fileX,fileY); // transform into GUI coord.
	aList.a[counter].Xposition = resX+10*i;
	aList.a[counter].Yposition = resY+10*i;
	aList.a[counter].state = dummyAircraft.getState();
	aList.a[counter].FuelValue = dummyAircraft.getActualFuel();
	
    }
}

/**
* This function transforms (X,Y) position of aircraft into
* (x,y) coordinates on the radar
* @param X X original position of aircraft
* @param Y Y original position of aircraft
* @param &x x positon on the radar, will be as a result
* @param &y y position on the radar, will be as a result
* @param R radius of airport
* @param r radius on the radar
* @param Xpos x position of the airport
* @param Ypos y position of the airport
*/
void transformIntoGUICoordinates(int X,int Y,int &x,int &y,int R,int r,int Xpos,int Ypos)
{	
    float prop;
    float tempx,tempy;
    
    prop = r/(R*1.0); // it is a proportion of radios of the radar to range of airport
    
    
    if(X>Xpos && Y<Ypos)
    {
	// We are in the first quarter
	tempx = X-Xpos;
	tempy = Ypos-Y;
	x = prop*tempx;
	y = prop*tempy;
	x = 350+x;
	y = 350-y;
    }
    else
    if(X>Xpos && Y>Ypos)
    {
	// We are in the second quarter
	tempx = X-Xpos;
	tempy = Y-Ypos;
	x = prop*tempx;
	y = prop*tempy;
	x = 350+x;
	y = 350+y;
    }
    else
    if(X<Xpos && Y>Ypos)
    {
	// We are in the third quarter
	tempx = Xpos-X; 
	tempy = Y-Ypos;
	x = prop*tempx;
	y = prop*tempy;
	x = 350-x;
	y = 350+y;
    }
    else
    if(X<Xpos && Y<Ypos)
    {
	// We are in the fourth quarter
	tempx = Xpos-X;
	tempy = Ypos-Y;
	x = prop*tempx;
	y = prop*tempy;
	x = 350-x;
	y = 350-y;
    }
    
}

/**
* This function puts data into special structure
* @param &packet name of packet where we'll put data about airport
*/
void prepareAirportData(packet_t &packet)
{
	packet.who=AIRPORT; // airport is sending this packet
	packet.type=COMMAND_OK; // evr'y thing ok
	packet.lotnisko.airportName=fileAirportName;
	packet.lotnisko.X=fileX;
	packet.lotnisko.Y=fileY;
	packet.lotnisko.Range=fileRange;
	packet.lotnisko.Capacity=fileCapacity;
	packet.lotnisko.RunwaysNumber=fileNumOfRunways;
	packet.lotnisko.State=fileState;    
}


void prepareRunwaysData(packet_t &packet)
{
    // dummy
    packet.who=AIRPORT;
    packet.type=COMMAND_OK;
    
}

/**
* This function gets info about aircraft which sent data
* and after that checks if THIS aircraft exists in airport
* database, if YES - only updates [X,Y] coordinates and state
* otherwise adds new aircraft into vector of aircrafts
*/
void updateInfoAboutAircraft(packet_t &packet)
{

    Aircraft dA;
    int counter;
    Aircraft *plane;
    
    for(counter=0;counter < a->getAircraftsNumber(); counter++)
    {
	dA = a->getAircraftStructure(counter);
	if(strcmp(dA.getAircraftName(),packet.samolot.aircraftName)==0)
	{
	    dA.setX(packet.samolot.Xposition);
	    dA.setY(packet.samolot.Yposition);
	    dA.setActualFuel(packet.samolot.FuelValue);
	    dA.setState(packet.samolot.state);
	    return ;
	}
	
	// if aircraft hasn't been found
	// we add it into our vector
	plane = new Aircraft(packet.samolot.aircraftID,
			     packet.samolot.aircraftName,
			     packet.samolot.Xposition,
			     packet.samolot.Yposition,
			     packet.samolot.FuelValue,
			     packet.samolot.state);
	a->addAircraft(*plane);
	a->showAircraftsList();
	delete plane;
	return ;
    }
     
}

void deleteInfoAboutAircraft(packet_t &packet)
{
    a->deleteAircraft(packet.samolot.aircraftName);    
}



